package oss.ngocminh.fw.forum;

import java.io.IOException;
import java.io.Reader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import oss.ngocminh.fw.data.Post;

class VBulletinPageParser {

	private static final Pattern THREAD_TITLE_PATTERN = Pattern.compile("<a.+?>(.+?)</a>", Pattern.DOTALL);
	private static final Pattern THREAD_TITLE_WRAPPER_PATTERN = Pattern.compile("<(\\w+)\\s+class=\"threadtitle\".+?</\\1>", Pattern.DOTALL);
	private static final Pattern PAGE_LINK_PRETTY_URL_PATTERN = Pattern.compile("<a href=\".+?page(\\d+)\"");
	private static final Pattern PAGINATION_PATTERN = Pattern.compile("class=\"pagination_top\".+?class=\"pagetitle\"", Pattern.DOTALL);
	private static final Pattern TIME_PATTERN = Pattern.compile("\\d+:\\d+\\s+(AM|PM)");
	private static final Pattern DATE_PATTERN = Pattern.compile("\\d+-\\d+-\\d+");
	private static final Pattern USER_NAME_PATTERN = Pattern.compile("<strong>(.+?)</strong>");
	private static final Pattern ID_PATTERN = Pattern.compile(
			"class=\"[^\"]*postcontainer[^\"]*\"\\s+id=\"post_\\d+\"");
	private static final Pattern USERNAME_BLOCK_PATTERN = Pattern.compile(
			"class=\"username_container.+?class=\"postbody", Pattern.DOTALL);
	private static final Pattern TITLE_PATTERN = Pattern.compile(
			"<h2 class=\"[^\"]*posttitle[^\"]*\".+?</h2>", Pattern.DOTALL);
	private static final Pattern CONTENT_PATTERN = Pattern.compile(
			"<\\w+\\s+class=\"postcontent.+?class=\"postfoot", Pattern.DOTALL);
	private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd-yyyy hh:mm a");
	
	public static class Result {
		private List<Post> posts;
		private int pageCount;
		private String threadTitle;

		public List<Post> getPosts() {
			return posts;
		}

		private void setPosts(List<Post> posts) {
			this.posts = posts;
		}

		public int getPageCount() {
			return pageCount;
		}

		public String getThreadTitle() {
			return threadTitle;
		}

		private void setPageCount(int pageCount) {
			this.pageCount = pageCount;
		}

		public void setThreadTitle(String threadTitle) {
			this.threadTitle = threadTitle;
		}
	}
	
	public Result parse(Reader reader) throws ParseException, IOException {
		return parseByScanner(reader);
	}
	
	private Result parseByScanner(Reader reader) throws ParseException {
		Result result = new Result();
		Scanner scanner = new Scanner(reader);
		result.setPageCount(parsePageCount(scanner));
		result.setThreadTitle(parseThreadTitle(scanner));
		result.setPosts(new ArrayList<Post>());
		Post post;
		while ((post = parseNextPost(scanner)) != null) {
			result.getPosts().add(post);
		}
		return result;
	}

	/*
	 * TODO: match page links without pretty URL???
	 */
	private int parsePageCount(Scanner scanner) {
		String html = scanner.findWithinHorizon(PAGINATION_PATTERN, 0);
		Matcher matcher = PAGE_LINK_PRETTY_URL_PATTERN.matcher(html);
		int pageCount = 1;
		while (matcher.find()) {
			pageCount = Integer.parseInt(matcher.group(1));
		}
		return pageCount;
	}
	
	private String parseThreadTitle(Scanner scanner) {
		String html = scanner.findWithinHorizon(THREAD_TITLE_WRAPPER_PATTERN, 0);
		Matcher matcher = THREAD_TITLE_PATTERN.matcher(html);
		if (matcher.find()) {
			return matcher.group(1);
		}
		return null;
	}

	private Post parseNextPost(Scanner scanner) throws ParseException {
		String id = parseId(scanner);
		if (id == null) {
			return null;
		}
		Date date = parsePostDate(scanner);
		String userName = parseUserName(scanner);
		String title = parsePostTitle(scanner);
		String content = parseContent(scanner);
		return new Post(id, userName, date, title, content);
	}

	private String parseContent(Scanner scanner) {
		String html = scanner.findWithinHorizon(CONTENT_PATTERN, 0);
		html = AdapterUtils.extractFirstElement(html);
		html = AdapterUtils.getInnerHTML(html);
		return html;
	}

	private Date parsePostDate(Scanner scanner) throws ParseException {
		String html = scanner.findWithinHorizon(Pattern.compile(
				"class=\"posthead.+?class=\"userinfo", Pattern.DOTALL), 0);
		return parsePostDate(html);
	}

	private String parseId(Scanner scanner) {
		String id = scanner.findWithinHorizon(ID_PATTERN, 0);
		if (id == null) {
			return null;
		}
		return parseId(id);
	}

	private String parseUserName(Scanner scanner) {
		String html = scanner.findWithinHorizon(USERNAME_BLOCK_PATTERN, 0);
		Matcher matcher = USER_NAME_PATTERN.matcher(html);
		matcher.find();
		return matcher.group(1);
	}

	private String parsePostTitle(Scanner scanner) {
		String title = scanner.findWithinHorizon(TITLE_PATTERN, 0);
		if (title == null) {
			return null;
		}
		title = title.replaceAll("</?.+>", "").trim();
		return title;
	}

//	private List<Post> parseByJsoup(String url, InputStream is)
//			throws IOException, ParseException {
//		List<Post> posts = new ArrayList<Post>();
//		long start = System.currentTimeMillis();
//		Document document = Jsoup.parse(is, "UTF-8", url);
//		long stop = System.currentTimeMillis();
//		System.out.println(stop-start);
//		Elements postContainers = document.select("li.postcontainer");
//		for (Element element : postContainers) {
//			posts.add(parsePost(element));
//		}
//		return posts;
//	}
//
//	private Post parsePost(Element element) throws ParseException {
//		String id = parseId(element.attr("id"));
//		String userName = element.select(".username_container strong").text();
//		Date postDate = parsePostDate(element.select(".postdate").html());
//		String title = element.select(".posttitle").text();
//		String content = element.select(".content").html();
//		return new Post(id, userName, postDate, title, content);
//	}

	private Date parsePostDate(String dateTimeStr) throws ParseException {
		String date = search(dateTimeStr, DATE_PATTERN);
		if (date == null) {
			throw new ParseException("Date string not found in \"" + dateTimeStr + "\"", 0);
		}
		String time = search(dateTimeStr, TIME_PATTERN);
		if (time == null) {
			throw new ParseException("Time string not found in \"" + dateTimeStr + "\"", 0);
		}
		dateTimeStr = date + " " + time;
		Date postDate = DATE_FORMAT.parse(dateTimeStr);
		return postDate;
	}

	private String search(String str, Pattern pattern) {
		Matcher matcher = pattern.matcher(str);
		if (!matcher.find()) {
			return null;
		}
		String date = matcher.group();
		return date;
	}

	private String parseId(String id) {
		return id.substring(id.lastIndexOf('_') + 1);
	}
}
